﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarbonExchange.AmeeQuery;
using CarbonExchange.Dal;

namespace CarbonExchange.Bll
{
    public class FootprintSaving : ItemBase
    {
        public int UserId { get; internal set; }
        public string FirstName { get; internal set; }
        public string LastName { get; internal set; }
        public decimal LatestFootprint { get; internal set; }
        public decimal? PreviousFootprint { get; internal set; }
        public decimal MaxFootprint { get; internal set; }
        public decimal? PotentialSaving { get; internal set; }
        public Country Country { get; internal set; }
        public string State { get; internal set; }

        public string Region
        {
            get
            {
                StringBuilder tmp = new StringBuilder();
                if(!string.IsNullOrEmpty(State))
                {
                    tmp.Append(State);
                    tmp.Append(", ");
                }
                if (Country != null)
                {
                    tmp.Append(Country.Name);
                }
                return tmp.ToString();
            }
        }

        public string DisplayName
        {
            get
            {
                return string.Concat(FirstName, " ", LastName);
            }
        }

        public string PublicDisplayName
        {
            get
            {
                return Anon ? "Anonymous User" : string.Concat(FirstName, " ", LastName);
            }
        }

        public bool Anon{get; internal set;}

        public decimal? SavedLast
        {
            get
            {
                if (PreviousFootprint.HasValue)
                {
                    return PreviousFootprint.Value - LatestFootprint;
                }
                else
                {
                    return null;
                }
            }
        }

        public decimal SavedTotal
        {
            get
            {
                return MaxFootprint - LatestFootprint;
            }
        }
    }

    public class FootprintSavingFactory : FactoryReadOnlyBase<FootprintSaving>
    {
        public FootprintSavingFactory() : base() { }

        public FootprintSavingFactory(IAmeeCommand ameeCommand) : base(ameeCommand) { }

        public FootprintSavingFactory(Context dalContext) : base(dalContext) { }

        public FootprintSavingFactory(IAmeeCommand ameeCommand, Context dalContext) : base(ameeCommand, dalContext) { }

        internal class JoinHelper
        {
            public Dal.AcmsUser User { get; set; }
            public decimal LatestFootprint { get; set; }
            public decimal? PreviousFootprint { get; set; }
            public decimal MaxFootprint { get; set; }
            public decimal? PotentialSaving { get; set; }
            public Dal.Country Country { get; set; }
        }

        public decimal GetCurrentPotentialSaving(int userId)
        {
            var latestAudit = DalContext.Audits.Where(x => x.UserId == userId && x.Completed && !x.Deleted).OrderByDescending(x => x.EndDate).Select(x => (DateTime?)x.EndDate).FirstOrDefault();
            decimal? data = DalContext.Commitments.Where(commitment => commitment.UserId == userId && commitment.CommitmentDate.HasValue && (commitment.CommitmentDate.Value > (latestAudit ?? DateTime.Parse("1/1/1753 12:00:00 AM")))).Sum(commitment => (decimal?)commitment.Pledge.AveSavingPerMonth);
            return data ?? 0;
        }

        private IQueryable<JoinHelper> GetSelection()
        {
            var users = DalContext.AcmsUsers.Where(x => x.Audits.Where(y => y.Completed).Count() > 0);
            var tmp = users.Select(user => new JoinHelper() 
            { 
                User = user, 
                //latest footprint == latest completed audit then sum footprint of answeritems
                LatestFootprint = user.Audits.Where(audit => audit.Completed).OrderByDescending(audit => audit.EndDate).First().Answers.Sum(answer => answer.AnswerItems.Sum(answerItem => answerItem.Footprint ?? 0m)) ,
                //previous footprint == if more than one completed audit skip latest and take next then sum footprint of answeritems
                PreviousFootprint = user.Audits.Where(audit => audit.Completed).Count() > 1 ? user.Audits.Where(audit => audit.Completed).OrderByDescending(audit => audit.EndDate).Skip(1).First().Answers.Sum(answer => answer.AnswerItems.Sum(answerItem => answerItem.Footprint ?? 0m)) : new Nullable<decimal>(),
                //max footprint == completed audit - max footprints when summing answer items
                MaxFootprint = user.Audits.Where(audit => audit.Completed).Max(audit => audit.Answers.Sum(answer => answer.AnswerItems.Sum(answerItem => answerItem.Footprint ?? 0m))),
                //MaxFootprint = user.Audits.Where(audit => audit.Completed).OrderByDescending(audit => audit.Answers.Sum(answer => answer.AnswerItems.Sum(answerItem => answerItem.Footprint ?? 0m))).First().Answers.Sum(z => z.AnswerItems.Sum(n => n.Footprint ?? 0m))
                //potential saving = allow commitments that have been made since the audit
                PotentialSaving = user.Commitments.Where(commitment => commitment.CommitmentDate.HasValue && commitment.CommitmentDate.Value > user.Audits.Where(audit => audit.Completed).OrderByDescending(audit => audit.EndDate).Select(x => x.EndDate).First()).Sum(commitment => commitment.Pledge.AveSavingPerMonth),
                //CustomInt1 holds country Id
                Country = DalContext.Countries.SingleOrDefault(country => country.Id == user.CustomInt1)
            });
            return tmp;
        }

        public int GetTotalCount(int siteId)
        {
            return GetSelection().Where(x => (x.User.SiteId ?? siteId) == siteId).Count();
        }

        public List<FootprintSaving> Get(string sort, int siteId, int startIndex, int maxRecords)
        {
            var tmp = GetSelection().Where(x => (x.User.SiteId ?? siteId) == siteId);
            if (sort != null)
            {
                var sorts = sort.Split(',');
                //bool sorted = false;
                foreach (string tmpSort in sorts)
                {
                    bool assending = false;
                    string field = tmpSort.Trim().ToLower();
                    if (tmpSort.Trim().ToLower().EndsWith(" asc"))
                    {
                        assending = true;
                        field = field.Substring(0, field.IndexOf(" asc"));
                    }
                    else if(tmpSort.Trim().ToLower().EndsWith(" desc"))
                    {
                        field = field.Substring(0, field.IndexOf(" desc"));
                    }
                    switch (field.ToLower())
                    {
                        case "publicdisplayname":
                            //custom bool 1 is anon
                            tmp = assending ? tmp.OrderBy(x => (x.User.CustomBool1 ?? false) ? "Anonymous User" : string.Concat(x.User.LastName, x.User.FirstName)) : tmp.OrderByDescending(x => (x.User.CustomBool1 ?? false) ? "Anonymous User" : string.Concat(x.User.LastName, x.User.FirstName));
                            break;
                        case "displayname":
                            tmp = assending ? tmp.OrderBy(x => string.Concat(x.User.LastName, x.User.FirstName)) : tmp.OrderByDescending(x => string.Concat(x.User.LastName, x.User.FirstName));
                            break;
                        case "savedlast":
                            //if null set val to min or max value to force them to the end
                            tmp = assending ? tmp.OrderBy(x => x.PreviousFootprint.HasValue ? x.PreviousFootprint.Value - x.LatestFootprint : decimal.MaxValue) : tmp.OrderByDescending(x => x.PreviousFootprint.HasValue ? x.PreviousFootprint.Value - x.LatestFootprint : decimal.MinValue);
                            break;
                        case "savedtotal":
                            tmp = assending ? tmp.OrderBy(x => x.MaxFootprint - x.LatestFootprint) : tmp.OrderByDescending(x => x.MaxFootprint - x.LatestFootprint);
                            break;
                        case "latestfootprint":
                            tmp = assending ? tmp.OrderBy(x => x.LatestFootprint) : tmp.OrderByDescending(x => x.LatestFootprint);
                            break;
                        case "region":
                            tmp = assending ? tmp.OrderBy(x => x.Country.Name).ThenBy(x => x.User.CustomString2) : tmp.OrderByDescending(x => x.Country.Name).ThenByDescending(x => x.User.CustomString2);
                            break;
                        case "potentialsaving":
                            tmp = assending ? tmp.OrderBy(x => x.PotentialSaving) : tmp.OrderByDescending(x => x.PotentialSaving);
                            break;
                        default:
                            throw new ApplicationException(string.Format("Sort for field '{0}' not implimented!", sort));
                    }
                }
            }
            else
            {
                tmp = tmp.OrderBy(x => x.LatestFootprint);
            }
            return tmp.Skip(startIndex).Take(maxRecords).Convert().ToList();
        }


        public FootprintSaving GetByUserId(int userId)
        {
            var tmp = GetSelection().Where(x => x.User.Id == userId).SingleOrDefault();
            return tmp.Convert();
        }
    }

    internal static class FootprintSavingConverter
    {
        internal static IQueryable<FootprintSaving> Convert(this IQueryable<FootprintSavingFactory.JoinHelper> source)
        {
            if (source != null)
                return source.Select(x => x.Convert());
            else
                return null;
        }

        internal static FootprintSaving Convert(this FootprintSavingFactory.JoinHelper source)
        {
            if (source != null)
                return new FootprintSaving()
                {
                    UserId = source.User.Id,
                    FirstName = source.User.FirstName,
                    LastName = source.User.LastName,
                    //Custom Bool 1 holds Anon
                    Anon = source.User.CustomBool1 ?? false,
                    LatestFootprint = source.LatestFootprint * 12,
                    PreviousFootprint = source.PreviousFootprint * 12,
                    MaxFootprint = source.MaxFootprint * 12,
                    PotentialSaving = source.PotentialSaving,
                    //CustomString2 holds state -- [State], [Country]
                    State = source.User.CustomString2,
                    Country = source.Country.Convert()
                };
            else
                return null;
        }
    }

}
